package findandrepl;

import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import javax.swing.AbstractAction;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JTextPane;

import textArea.AwesomeTextPanel;

import document.SyntaxHighlighter;

/**
 * Find and Replace Panel
 * @author Ashley Polkinghorn
 *
 */
public class UserFindReplaceInput extends JPanel {

	final int textFieldSize = 20;
	JTextPane textPane;
	JTextField fromExpression;
	JTextField toExpression;
	
	JButton findButton;
	JButton replaceButton;
	JButton replaceAllButton;
	JButton closeButton;
	JLabel notifsLabel;
	JCheckBox regexCheck;
	AwesomeTextPanel panel;
	/**
	 * Create new F&R (create the GUI using GroupLayout).
	 * @param container
	 * @param pane
	 */
	public UserFindReplaceInput(Container container, JTextPane pane, AwesomeTextPanel panel)
	{
		this.panel = panel;
		textPane = pane;
		GroupLayout layout = new GroupLayout(this);
		this.setLayout(layout);
		layout.setAutoCreateGaps(true);
		layout.setAutoCreateContainerGaps(true);
		
		JLabel find = new JLabel();
		find.setText("Find:");
		JLabel to = new JLabel();
		to.setText("Replace with:");

		
		fromExpression = new JTextField(textFieldSize);
		toExpression = new JTextField(textFieldSize);
		findButton = new JButton(new FindAction());
		replaceButton = new JButton(new ReplaceAction());
		replaceAllButton = new JButton(new ReplaceAllAction());
		closeButton = new JButton(new CloseAction(container));
		regexCheck = new JCheckBox("Use regular expressions");
		notifsLabel = new JLabel();
		
		
		layout.setHorizontalGroup(
				layout.createSequentialGroup()
						.addGroup(layout.createParallelGroup()
								.addComponent(find)
								.addComponent(to)
								.addComponent(findButton)
								.addComponent(replaceAllButton)
								.addComponent(notifsLabel))
						.addGroup(layout.createParallelGroup()
								.addComponent(fromExpression)
								.addComponent(toExpression)
								.addComponent(replaceButton)
								.addComponent(closeButton)
								.addComponent(regexCheck))
		);
		layout.setVerticalGroup(
				layout.createSequentialGroup()
					.addGroup(layout.createParallelGroup()
						.addComponent(find)
						.addComponent(fromExpression))
					.addGroup(layout.createParallelGroup()
						.addComponent(to)
						.addComponent(toExpression))
					.addGroup(layout.createParallelGroup()
									.addComponent(findButton)
									.addComponent(replaceButton))
					.addGroup(layout.createParallelGroup()
									.addComponent(replaceAllButton)
									.addComponent(closeButton))
					.addGroup(layout.createParallelGroup()
							.addComponent(notifsLabel)
							.addComponent(regexCheck))
		
		);
		
		setVisible(true);
	}

	/**
	 * Find str in pane and highlight it.
	 * @param startIndex
	 * @param str
	 * @param pane
	 * @param regex
	 * @return
	 */
	private boolean findAndHighlight(int startIndex, String str, 
			JTextPane pane, boolean regex)
	{
		Indexes indexes = find(startIndex, str, pane, regex);
		if(indexes.start < -1) return false;
		if(indexes.start < 0)
		{
			/* Text is not found. Search from beginning? */
			notifsLabel.setText("Text not found.");
			return false;
		}
		
		textPane.setSelectionStart(indexes.start);
		textPane.setSelectionEnd(indexes.end);
		notifsLabel.setText("");
		return true;
	}
	
	/**
	 * Finds str in pane
	 * @param str
	 * @param pane
	 * @param regex
	 * @return -1 if not found. Returns -2 if text is empty.
	 */
	protected Indexes find(int startIndex, String str, JTextPane pane, boolean regex)
	{
		Indexes ind = new Indexes();
		if(str == null || str.length() == 0)
		{
			/* text box is empty */
			notifsLabel.setText("Hey! There's no text in that Find box!");
			ind.start = -2;
			return ind;
		}
		return findText(startIndex, str, pane, regex);
	}
	
	/**
	 * Finds str in pane (gives indices)
	 * @param startIndex
	 * @param str
	 * @param pane
	 * @param regex
	 * @return
	 */
	private Indexes findText(int startIndex, String str, JTextPane pane, boolean regex)
	{
		Indexes ind = new Indexes();
		String text = pane.getText();
		if(!regex)
		{
			ind.start = text.indexOf(str, startIndex);
			ind.end = ind.start + str.length();	
		}
		else
		{
			// try regex
			Pattern p;
			Matcher m;
			try
			{
				p = Pattern.compile(str);
				int l = text.length();
				if(l <= startIndex)
				{
					ind.start = -1;
					ind.end=-1;
					return ind;
				}
				String subStr = text.substring(startIndex,l);
				m = p.matcher(subStr);	
			}
			catch(PatternSyntaxException e)
			{
				notifsLabel.setText("Invalid regex: " + e.getMessage());
				ind.start = -2;
				ind.end = -2;
				return ind;
			}
			if(m.find())
			{
				ind.start = m.start() + startIndex;
				ind.end = startIndex + m.end();
			}
			else
			{
				ind.start = -1;
				ind.end = -1;
			}
		}
		return ind;
	}
	
	/**
	 * Replaces text in pane between startIndex and endIndex with newString
	 * @param newString
	 * @param startIndex
	 * @param endIndex
	 * @param pane
	 */
	private void replaceText(String newString, int startIndex, int endIndex, JTextPane pane)
	{
		if(newString == null || newString.length() == 0)
		{
			/* text box is empty */
			notifsLabel.setText("Hey! There's no text in that Replace box!");
		}
		String text = pane.getText();
		text = text.substring(0,startIndex) + newString+ text.substring(endIndex,text.length());
		pane.setText(text);
		SyntaxHighlighter.highlightDocument(pane,  this.panel.getDocumentType());
	}
	
	/**
	 * Replaces selected text in pane with str.
	 * @param str
	 * @param pane
	 * @param regex
	 */
	private void replaceSelected(String str, JTextPane pane, boolean regex)
	{
		int selStart = pane.getSelectionStart();
		int selEnd = pane.getSelectionEnd();

		if(selStart == selEnd)
		{
			// nothing selected. return
			return;
		}
		
		
		/* Replace item and look for more */
		replaceText(str,selStart, selEnd,pane);
		
		pane.setCaretPosition(selStart + str.length());
		
		notifsLabel.setText("");
	}
	
	/**
	 * Responds to Find button press
	 * @author Ashley Polkinghorn
	 *
	 */
	private class FindAction extends AbstractAction
	{
		/**
		 * Create new FindAction.
		 */
		public FindAction()
		{
			super("Find");
			putValue(MNEMONIC_KEY, new Integer(KeyEvent.VK_F));
		}
		/**
		 * Find button pressed.
		 */
		public void actionPerformed(ActionEvent arg0) {
			
			String fromStr = fromExpression.getText();
			findAndHighlight(textPane.getCaretPosition(),fromStr,textPane,regexCheck.isSelected());
		}
		
	}

	/**
	 * Responds to Replace button press
	 * @author Ashley Polkinghorn
	 *
	 */
	private class ReplaceAction extends AbstractAction
	{
		/**
		 * New ReplaceAction
		 */
		public ReplaceAction()
		{
			super("Replace");
			putValue(MNEMONIC_KEY, new Integer(KeyEvent.VK_R));
		}
		
		/**
		 * Replace button presed.
		 */
		public void actionPerformed(ActionEvent arg0)
		{
			String fromStr = fromExpression.getText();
			String toStr = toExpression.getText();
			
			int selStart = textPane.getSelectionStart();
			int selEnd = textPane.getSelectionEnd();

			if(selStart == selEnd)
			{
				// nothing selected. find something
				Indexes ind = find(textPane.getCaretPosition(),fromStr,textPane,regexCheck.isSelected());
				if(ind.start < -1) return;
				if(ind.start < 0)
				{
					/* Text is not found. Search from beginning? */
					notifsLabel.setText("Text not found.");
					return;
				}
				selStart = ind.start;
				selEnd = ind.end;
			}
			
			
			/* Replace item and look for more */
			replaceText(toStr,selStart, selEnd,textPane);
			
			textPane.setCaretPosition(selStart + toStr.length());
			
			
			Indexes ind = findText(textPane.getCaretPosition(),fromStr, textPane, regexCheck.isSelected());
			if(ind.start < 0) return;
			textPane.setSelectionStart(ind.start);
			textPane.setSelectionEnd(ind.end);
			
			notifsLabel.setText("");
		}
	}
	
	/**
	 * Responds to ReplaceAll button press.
	 * @author Ashley Polkinghorn
	 *
	 */
	private class ReplaceAllAction extends AbstractAction
	{
		/**
		 * New ReplaceAll.
		 */
		public ReplaceAllAction()
		{
			super("Replace All");
			putValue(MNEMONIC_KEY, new Integer(KeyEvent.VK_A));
		}

		/**
		 * ReplaceAll button pressed.
		 */
		public void actionPerformed(ActionEvent arg0)
		{
			String fromStr = fromExpression.getText();
			String toStr = toExpression.getText();
			
			if(fromStr == null || fromStr.length() == 0)
			{	/* text box is empty */
				notifsLabel.setText("Hey! There's no text in that Find box!");
				return;
			}
			
			int index=0;
			int foundCount = 0;
			while(findAndHighlight(index,fromStr,textPane,regexCheck.isSelected()))
			{
				foundCount++;
				index = textPane.getSelectionEnd();
				replaceSelected(toStr, textPane, regexCheck.isSelected());
			}
			notifsLabel.setText(foundCount + " match" + 
					(foundCount == 1 ?"":"es")+ " replaced." );
		}		
	}
	
	/**
	 * Responds to Close button press.
	 * @author Ashley Polkinghorn
	 *
	 */
	private class CloseAction extends AbstractAction
	{
		Container container;
		/**
		 * New CloseAction. Requres reference to dialogbox or
		 * other container that includes this panel.
		 * @param findandReplaceDialog
		 */
		public CloseAction(Container findandReplaceDialog)
		{
			super("Close");
			putValue(MNEMONIC_KEY, new Integer(KeyEvent.VK_C));
			container = findandReplaceDialog;
		}
		
		/**
		 * Close button pressed.
		 */
		public void actionPerformed(ActionEvent arg0)
		{
			container.setVisible(false);
		}
	}
	
	/**
	 * Keeps track of a selection by
	 *  its start and end indices.
	 * @author Ashley Polkinghorn
	 *
	 */
	private class Indexes
	{
		/**
		 * Start index
		 */
		int start;
		/**
		 * End index
		 */
		int end;
		
		/**
		 * Returns indices
		 */
		public String toString()
		{
			return "Ind:" + start + "," + end;
		}
	}
}
